﻿using System;
using System.Collections.Generic;

namespace ChivalryUnity.Fsm
{
    /// <summary>
    /// 状态机
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Fsm<T> where T : Fsm<T>
    {
        public State<T> CurrentState { get; private set; }
        public bool IsRunning { get; private set; }

        private List<State<T>> m_states;
        private State<T> m_defaultState;
        private Stack<State<T>> m_stateStack;

        public Fsm()
        {
            m_states = new List<State<T>>();
            m_stateStack = new Stack<State<T>>();
            m_transitions = new Queue<Transition<T>>();
        }

        /// <summary>
        /// 添加状态
        /// </summary>
        /// <param name="state">状态</param>
        /// <param name="isDefault">是否为默认状态</param>
        public void AddState(State<T> state, bool isDefault = false)
        {
            if (state == null)
            {
                throw new Exception("不能添加空状态");
            }
            if (GetType() != typeof(T))
            {
                throw new Exception("状态机类型不匹配,无法添加状态");
            }
            state.Fsm = this as T;
            m_states.Add(state);
            if (isDefault)
            {
                m_defaultState = state;
            }
        }

        /// <summary>
        /// 启动状态机
        /// </summary>
        public void Start()
        {
            if (IsRunning)
                return;

            if (m_defaultState == null)
            {
                throw new Exception("状态机没有设置默认状态");
            }

            IsRunning = true;
            ChangeState(m_defaultState);
        }

        /// <summary>
        /// 关闭状态机
        /// </summary>
        public void Stop()
        {
            if (!IsRunning)
                return;

            IsRunning = false;
            while (m_stateStack.Count > 0)
            {
                m_stateStack.Pop().Leave();
            }
        }

        /// <summary>
        /// 更新状态机
        /// </summary>
        public void Update()
        {
            if (CurrentState != null)
            {
                CurrentState.Update();
            }
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="arg"></param>
        public void SendMessage(string msg, object arg)
        {
            if (CurrentState != null)
            {
                CurrentState.HandleMessage(msg, arg);
            }
        }

        /// <summary>
        /// 切换状态
        /// </summary>
        /// <typeparam name="TState"></typeparam>
        public void ChangeState<TState, TFsm>() where TState : State<TFsm> where TFsm : Fsm<TFsm>
        {
            for (int i = 0; i < m_states.Count; i++)
            {
                if (m_states[i].GetType() == typeof(TState))
                {
                    ChangeState(m_states[i]);
                    break;
                }
            }
        }

        private bool m_isChangingState;
        private Queue<Transition<T>> m_transitions;

        /// <summary>
        /// 转换状态
        /// </summary>
        /// <param name="newState"></param>
        private void ChangeState(State<T> newState)
        {
            if (newState == null)
                return;

            m_transitions.Enqueue(new Transition<T>(newState));    //加入转换队列

            if (m_isChangingState)
                return;

            int transiteCount = 0;
            m_isChangingState = true;
            while (m_transitions.Count > 0)
            {
                if (CurrentState != null)
                {
                    CurrentState.Leave();
                }
                CurrentState = m_transitions.Dequeue().TargetState;
                m_stateStack.Push(CurrentState);
                CurrentState.Enter();

                //为了防止无限跳转,例如A->B,B->A
                transiteCount++;
                if (transiteCount >= 100)
                    throw new StackOverflowException("状态跳转出现死循环");
            }
            m_isChangingState = false;
        }
    }
}